home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 12 - 1996 / 12.03 Mar 96 / SlidingTiles ƒ / TileMain.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-15  |  10.1 KB  |  393 lines  |  [TEXT/CWIE]

  1. /* 
  2.   Test code for the SlidingTiles Programmer's Challenge
  3. */
  4.  
  5. /* undef ANIMATE to delete animation of your solution */
  6. #define ANIMATE 1
  7. //#undef ANIMATE
  8.  
  9. /* define MOVE_USER_TILE to also move the user's tile in the MakeMove callback */
  10. #undef MOVE_USER_TILE
  11. //#define MOVE_USER_TILE
  12.  
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16.  
  17. #include "Solve.h"
  18. #include "TileMain.h"
  19.  
  20. #define kNormalSize 20
  21. #define kOffset 20
  22. #define kMinWindowSize 300
  23. #define RECTWIDTH(r) ((r).right-(r).left)
  24. #define RECTHEIGHT(r) ((r).bottom-(r).top)
  25.  
  26. static long gNumRows,gNumCols;    /* initialized by test code */
  27. static long gEmptyRow,gEmptyCol;  /* initialized by test code */
  28. static long *gTiles;              /* initialized by test code */
  29.  
  30. static FontInfo gFontInfo;
  31. static WindowPtr gWind;
  32. static Rect gRect;
  33. static short gAnimateSpeed,gTileSize,gFontSize,gOffset;
  34.  
  35. #define TileValue(tiles,row,col,numCols) *(tiles+(row)*numCols+(col))
  36. #define OutOfRange(val,num)  (((val)<0) || ((val)>=(num)))
  37.  
  38. /*
  39.    Calculate the distance of the row/col tile from its correct position.
  40. */
  41. static long TileDist(long *tiles,long row,long col,long numCols) 
  42.   long row0,col0,val; 
  43.   val = TileValue(tiles,row,col,numCols); 
  44.   row0 = val / numCols; 
  45.   col0 = val - row0*numCols;
  46.   return abs(row-row0) + abs(col-col0); 
  47. }
  48.   
  49. /*
  50.    Calculate a score for this position, the sum of all tile distances
  51.    from their correct position.
  52. */
  53. static long CalcScore(long *tiles,long numRows,long numCols) 
  54.   long row,col,dist,cumDist=0;
  55.   for (row=0; row<numRows; ++row) {
  56.     for (col=0; col<numCols; ++col) {
  57.       if ((row==0) && (col==0)) continue;
  58.       dist = TileDist(tiles,row,col,numCols);
  59.       cumDist += dist;
  60.     }
  61.   }
  62.   return cumDist;
  63. }
  64.  
  65. /*
  66.    Draw a status string in the animation window.
  67. */
  68. static void MyDrawString(StringPtr str)
  69. {
  70.   Rect r;
  71.   
  72.   SetRect(&r,5,5,160,15);
  73.   EraseRect(&r);
  74.   MoveTo(r.left,r.bottom);
  75.   TextSize(9);
  76.   DrawString(str);
  77.   TextSize(gFontSize);
  78. }
  79.  
  80. /*
  81.    Draw the position score in the animation window.
  82. */
  83. static void DrawScore(long dist)
  84. {
  85.   Str255 str;
  86.   sprintf((char *)str,"Distance from solution: %ld",dist);
  87.   c2pstr((char *)str);
  88.   MyDrawString(str);
  89. }
  90.  
  91. /*
  92.    Draw one tile.
  93. */
  94. static void DrawOneTile(long *tiles,long theRow,long theCol)
  95. {
  96.   long val,wid;
  97.   Rect r;
  98.   Str255 valStr;
  99.   
  100.   SetRect(&r,gOffset,gOffset,gOffset+gTileSize,gOffset+gTileSize);
  101.   val = TileValue(tiles,theRow,theCol,gNumCols);
  102.   OffsetRect(&r,gTileSize*theCol,gTileSize*theRow);
  103.   if (val != 0) {
  104.     PenSize(2,2);
  105.     FrameRect(&r);
  106.     PenNormal();
  107.     NumToString(val,valStr);
  108.     InsetRect(&r, 1, 1);
  109.     EraseRect(&r);
  110.     wid = StringWidth(valStr);
  111.     MoveTo(gOffset + theCol*gTileSize+gTileSize/2-wid/2,gOffset + theRow*gTileSize+(gTileSize+gFontInfo.ascent)/2);
  112.     DrawString(valStr);
  113.   } else {
  114.     PaintRect(&r);
  115.   }
  116. }
  117.  
  118. /*
  119.    Draw all tiles
  120. */
  121. static void DrawTiles(WindowPtr wind)
  122. {
  123.   GrafPtr savePort;
  124.   long row,col;
  125.   Rect r;
  126.   GetPort(&savePort);
  127.   SetPort(wind);
  128.   EraseRect(&((GrafPtr)wind)->portRect);
  129.   SetRect(&r,gOffset-1,gOffset-1,gOffset+1+gNumCols*gTileSize,gOffset+1+gNumRows*gTileSize);
  130.   FrameRect(&r);
  131.   for (row=0; row<gNumRows; ++row) {
  132.     for (col=0; col<gNumCols; ++col) {
  133.       DrawOneTile(gTiles,row,col);
  134.     }
  135.   }
  136.   SetPort(savePort);
  137. }
  138.  
  139. /*
  140.    Initialize all tiles in their correct positions.  Allocate storage.
  141. */
  142. static long *InitTiles(long numRows, long numCols)
  143. {
  144. int row,col;
  145. long index = 0;
  146. long *gtiles = (long *)NewPtr(numRows*numCols*sizeof(long));
  147. long *tiles = gtiles;
  148.   if (nil != gtiles) {
  149.     for (row=0; row<numRows; ++row) {
  150.       for (col=0; col<numCols; ++col)
  151.         *tiles++ = index++;
  152.     }
  153.   }
  154.   return gtiles;
  155. }
  156.  
  157. /*
  158.    Dispose of tile storage.
  159. */
  160. static void DisposeTiles(long *tiles) 
  161. {
  162.   DisposePtr((Ptr)tiles);
  163. }
  164.  
  165. #define kMoveLeftRight 0x01
  166. #define kMoveUpLeft 0x02
  167.  
  168. #define Swap(tiles,a,b) \
  169. { long temp; \
  170.   temp=tiles[a];tiles[a]=tiles[b];tiles[b]=temp; \
  171. }
  172.  
  173. static void ExpressScramble(long *tiles,long numRows,long numCols) {
  174. //Scrambles a sliding tile puzzle of any size quickly.
  175. //(Courtesy of Ernst Munter)
  176. long i,b,numSwaps=0;
  177.   i=numRows*numCols;
  178.   do {
  179.     --i;
  180.     b=1+(rand()%i);
  181.     if (b!=i) {
  182.       Swap(tiles,i,b);
  183.       numSwaps++;
  184.     }
  185.   } while (i>3);
  186.   if (numSwaps&1) Swap(tiles,1,2);
  187.  
  188. //at this point the puzzle is scrambled, with every
  189. //possible legal (solvable) state reached with equal
  190. //probability, EXCEPT the blank tile is still at gTiles[0].
  191. //No unsolvable states can be reached with this algorithm.
  192.  
  193. //To move the blank tile inside the puzzle, use MakeMove
  194. //just a few times, say 1 or 2 times the number of tiles.
  195.  
  196. }
  197.  
  198. /*
  199.    Scramble the puzzle by moving the blank tile a bunch of times.
  200. */
  201. static void ScrambleTiles(long numIters, MoveProc Move)
  202. {
  203. long dir,i,rowToMove,colToMove;
  204. static long prevDir = -1;
  205. Boolean legal;
  206.   MoveTo(160,15);
  207.   DrawString("\p SCRAMBLING ...");
  208.   for (i=0; i<numIters; ++i) {
  209.     do {
  210.       rowToMove = gEmptyRow;
  211.       colToMove = gEmptyCol;
  212.       dir = Random()&0x03;
  213. /* dir ==  0x00                           move Up tile down into blank
  214.        ==  kMoveLeftRight                 move Left tile right into blank
  215.        ==  kMoveUpLeft                    move Down tile up into blank
  216.        ==  kMoveLeftRight+kMoveDownRight  move Right tile left into blank */
  217.       if (dir & kMoveLeftRight) /* move Left Or Right */ {
  218.         if (dir & kMoveUpLeft)  /* try to move tile left into empty square */ {
  219.           if (gEmptyCol < gNumCols-1)  ++colToMove;
  220.           else                         {dir ^= kMoveUpLeft; --colToMove;}
  221.         } else {                /* try to move tile right into empty square */
  222.           if (gEmptyCol > 0)           --colToMove;
  223.           else                         {dir ^= kMoveUpLeft; ++colToMove;}
  224.         }
  225.       } else {                  /* move Up or Down */
  226.         if (dir & kMoveUpLeft)  /* try to move tile up into empty square */ {
  227.           if (gEmptyRow < gNumRows-1)  ++rowToMove;
  228.           else                        {dir ^= kMoveUpLeft; --rowToMove;}
  229.         } else {                /* try to move tile down into empty square */
  230.           if (gEmptyRow > 0)           --rowToMove;
  231.           else                         {dir ^= kMoveUpLeft; ++rowToMove;}
  232.         }
  233.       }
  234.     } while ( (dir^prevDir) == kMoveUpLeft ); /* avoid sequential moves that cancel */
  235.     prevDir = dir;
  236.     legal = (*Move)(rowToMove,colToMove);
  237.     
  238.   }
  239. }
  240.  
  241. /*
  242.    Determine if the puzzle is in the pristine state.
  243. */
  244. static Boolean SolutionIsCorrect(long *tiles,long numRows,long numCols)
  245. {
  246.   long numTiles = numRows*numCols;
  247.   while (0 <= --numTiles)
  248.     if (numTiles != *(tiles+numTiles)) return false;
  249.   return true;
  250. }
  251.  
  252. /*
  253.    Update tile window.
  254. */
  255. void DoUpdate(WindowPtr wind)
  256. {
  257.   BeginUpdate(wind);
  258. #ifdef ANIMATE
  259.   DrawTiles(wind);
  260. #endif
  261.   EndUpdate(wind);
  262. }
  263.   
  264. /*
  265.    Callback routine to move tile.
  266. */
  267. static Boolean MakeMove(long tileToMoveRow,long tileToMoveCol) 
  268. {
  269.   long diff;
  270.   if (OutOfRange(tileToMoveRow,gNumRows)) return false;
  271.   if (OutOfRange(tileToMoveCol,gNumCols)) return false;
  272.   if (tileToMoveRow == gEmptyRow) {
  273.     diff = tileToMoveCol - gEmptyCol;
  274.   } else if (tileToMoveCol == gEmptyCol) {
  275.     diff = tileToMoveRow - gEmptyRow;
  276.   } else {
  277.     return false;
  278.   }
  279.   if ((diff != -1) && (diff != 1)) return false;
  280.   TileValue(gTiles,gEmptyRow,gEmptyCol,gNumCols) = 
  281.     TileValue(gTiles,tileToMoveRow,tileToMoveCol,gNumCols);
  282. #ifdef ANIMATE
  283.   if (gAnimateSpeed>0) {
  284.     DrawOneTile(gTiles,gEmptyRow,gEmptyCol);
  285.   }
  286. #endif
  287.   gEmptyRow = tileToMoveRow;
  288.   gEmptyCol = tileToMoveCol;
  289.   TileValue(gTiles,gEmptyRow,gEmptyCol,gNumCols) = 0;
  290. #ifdef ANIMATE
  291.   if (gAnimateSpeed>0) {
  292.     DrawOneTile(gTiles,gEmptyRow,gEmptyCol);
  293.     Delay(gAnimateSpeed,&diff);
  294.     DrawScore(CalcScore(gTiles,gNumRows,gNumCols));
  295.   }
  296. #endif
  297.   return true;
  298. }
  299.  
  300. /*
  301.    Execute one test case.
  302. */
  303. void RunOneCase(WindowPtr wind,long numRows,long numCols)
  304. {
  305.   GDHandle gd;
  306.   EventRecord ev;
  307.   long *userTiles;
  308.   short width,height,mBarHeight,hPos,vPos;
  309.   
  310. /* Initialize */
  311.   gWind = wind;
  312.   gNumRows = numRows;
  313.   gNumCols = numCols;
  314.   gEmptyRow = gEmptyCol = 0;
  315.   gTiles = InitTiles(gNumRows,gNumCols);
  316.   if (gTiles==nil) DebugStr("\p problem allocating tile memory");
  317.   gFontSize = 9;
  318.   if (numRows>numCols)   gTileSize = (kMinWindowSize-2*kOffset)/numRows;
  319.   else                   gTileSize = (kMinWindowSize-2*kOffset)/numCols;
  320.   gFontSize = gTileSize/2;
  321.   if (gTileSize<kNormalSize) {
  322.     gTileSize = kNormalSize;
  323.     gFontSize = 9;
  324.   }
  325.   TextSize(gFontSize);
  326.   GetFontInfo(&gFontInfo);
  327.   
  328. /* Adjust animation window size */
  329.   width = 2*kOffset + gTileSize*numCols;
  330.   height = 2*kOffset + gTileSize*numRows;
  331.   gOffset = kOffset;
  332.  
  333.   if (width<kMinWindowSize) {
  334.     width = kMinWindowSize;
  335.     gOffset = (kMinWindowSize - gTileSize*numCols)/2;
  336.   }
  337.   if (height<kMinWindowSize) {
  338.     height = kMinWindowSize;
  339.     gOffset = (kMinWindowSize - gTileSize*numRows)/2;
  340.   }
  341.   width = 2*gOffset + gTileSize*numCols;
  342.   height = 2*gOffset + gTileSize*numRows;
  343.   
  344. /* Adjust animation window position */
  345.   mBarHeight = LMGetMBarHeight();
  346.   gd = GetMainDevice();
  347.   gRect = (**gd).gdRect;
  348.   hPos = (RECTWIDTH(gRect)-width)/2;
  349.   vPos = mBarHeight+(RECTHEIGHT(gRect)-height-mBarHeight)/2;
  350.   if (hPos<0) hPos = 0;
  351.   if (vPos<mBarHeight) vPos=mBarHeight;
  352.   MoveWindow(gWind,hPos,vPos,false);
  353.   SizeWindow(gWind,width, height, true);
  354.   ShowWindow(gWind);
  355.   SetPort(gWind);
  356.  
  357. /* Initialize tiles */
  358.   gAnimateSpeed=0;
  359.   ExpressScramble(gTiles,numRows,numCols);
  360.   ScrambleTiles(numRows*numCols*40L,MakeMove);
  361.   DrawScore(CalcScore(gTiles,gNumRows,gNumCols));
  362.   DrawTiles(gWind);
  363.   gAnimateSpeed=4;
  364.  
  365.   userTiles = (long *)NewPtr(gNumRows*gNumCols*sizeof(long));
  366.   if (userTiles==nil) DebugStr("\p problem allocating user tile memory");
  367.   memcpy(userTiles,gTiles,gNumRows*gNumCols*sizeof(long));
  368.  
  369. /*
  370.   CALL USER SOLUTION
  371. */
  372.   SolveTiles(userTiles,gNumRows,gNumCols,MakeMove);
  373.  
  374.  
  375. /* Determine if solution is correct */
  376.   if (SolutionIsCorrect(gTiles,gNumRows,gNumCols)) {
  377.     MyDrawString("\p Solution is correct, hit a key to continue.");
  378.   } else {
  379.     MyDrawString("\p Solution is NOT correct, hit a key to continue.");
  380.   }
  381. /* Wait for acknowledgement */
  382.   SysBeep(1);
  383.   do { ;
  384.   } while (!WaitNextEvent(keyDownMask,&ev,0,nil));
  385.  
  386. /* Clean up and leave */
  387.   HideWindow(gWind);
  388.   DisposeTiles(userTiles);
  389.   DisposeTiles(gTiles);
  390. }
  391.